package han.fetch;

import com.han.http.client.exception.RequestNotExecutedException;
import com.han.http.client.paramter.RequestHeader;
import han.entity.FinanceProjectIdEntity;
import han.entity.FinanceProjectLabelEntity;
import han.entity.InvestCompanyIdEntity;
import han.exception.MuchRequestException;
import han.exception.NullAvailableCookieException;
import han.exception.OtherException;
import han.exception.UnLoginException;
import han.parser.FinanceProjectIdParser;
import han.parser.InvestOrganizationIdParser;
import han.proof.Cookie;
import han.proof.CookieRepository;
import han.sql.InsertThread;
import han.sql.Operator;
import han.thread.CustomThread;
import han.thread.MonitorThread;
import han.util.ListTool;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import static com.han.http.util.Print.printlnErr;
import static com.han.http.util.Print.printlnInfo;

/**
 * @fileName: FinanceProjectId
 * @author: Han
 * @date: 2018/6/25 17:51
 * @dscription: TODO
 */
class FinanceProjectId {

    /**
     * 0:正常获取,1:暂时封禁
     */
    final static String[] RESPONSE_CODE = {
            "\"code\":0",
            "\"code\":429",
            "\"msg\":\"已达到今日浏览项目上限\"",
            "\"msg\":\"公司暂未收录\"",
            "\"msg\":\"你的账号已在其他设备登录\"",
    };
    /**
     * 已访问链接个数
     */
    private int requestedCount = 0;
    /**
     * 抓取对象
     */
    private Fetcher fetcher;
    /**
     * 数据库对象
     */
    private Operator operator;
    /**
     * cookie仓库对象
     */
    private CookieRepository cookieRepository;
    /**
     * 手机端融资项目页码
     */
    private int phonePage;
    /**
     * 同步锁
     */
    private Object krLock = new Object();

    /**
     * 手机链接
     *
     * @param cookieRepository cookie库
     * @param phonePage        总页码
     */
    FinanceProjectId(CookieRepository cookieRepository, int phonePage) {
        fetcher = new Fetcher();
        operator = Operator.getInstance();
        this.cookieRepository = cookieRepository;
        this.phonePage = phonePage;
    }

    FinanceProjectId(CookieRepository cookieRepository) {
        fetcher = new Fetcher();
        operator = Operator.getInstance();
        this.cookieRepository = cookieRepository;
    }

    /**
     * 线程启动类
     */
    void threadStart() {
        //定义标签list
        List<FinanceProjectLabelEntity> labelList = operator.queryFinanceProjectLabel();
        //切割
        List<List<FinanceProjectLabelEntity>> splitList = new ListTool<FinanceProjectLabelEntity>()
                .splitListBySize(labelList, 80);
        //获取cookie list
        List<Cookie> cookieList = cookieRepository.getCookieList();
        //获取线程池
        ThreadPoolExecutor executor = new CustomThread();
        //执行插入线程
        InsertThread insertThread = new InsertThread();
        executor.submit(insertThread);
        //监控线程
        MonitorThread monitorThread = new MonitorThread(insertThread, executor, cookieRepository);
        executor.submit(monitorThread);
        for (int i = 0; i < splitList.size() && i < cookieList.size(); i++) {
            //获取标签
            List<FinanceProjectLabelEntity> tempLabelList = splitList.get(i);
            //获取cookie
            Cookie cookie = cookieList.get(i);
            //实例化任务类
            FinanceProjectIdThread thread = new FinanceProjectIdThread(cookie, tempLabelList, insertThread, krLock);
            //提交任务
            executor.submit(thread);
            //休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //关闭线程池
        executor.shutdown();
    }

    private void getCompanyId() {
        int[] areas = {
                101, 109, 21903, 21901, 21101, 22301, 102, 90000, 122, 112, 113, 128, 119, 120, 124, 121, 103, 116,
                108, 117, 118, 107, 110, 114, 106, 130, 129, 115, 104, 127, 123, 125, 111, 105, 131, 126, 133, 134, 132
        };
        Cookie cookie = cookieRepository.getCookie();
        if (Objects.isNull(cookie)) {
            printlnErr("没有可用Cookie,退出");
            return;
        }
        for (int areaId : areas) {
            try {
                cookie = getCompanyIdByArea(areaId, cookie, 0);
                printlnInfo(areaId + "抓取完成");
            } catch (NullAvailableCookieException e) {
                break;
            }
        }
    }

    /**
     * 根据区域获取公司id
     *
     * @param areaId 区域id
     * @param cookie cookie
     * @param flag   0:抓取融资项目id,1:抓取投资公司id
     * @return 可用cookie
     * @throws NullAvailableCookieException 没有可用cookie
     */
    Cookie getCompanyIdByArea(int areaId, Cookie cookie, int flag) throws NullAvailableCookieException {
        String rowUrlStr = "";
        if (flag == 0) {
            rowUrlStr = "https://rong.36kr.com/n/api/column/0/company?city=" + areaId +
                    "&industry=MEDICAL_HEALTH&sortField=HOT_SCORE&p=";
        } else if (flag == 1) {
            rowUrlStr = "https://rong.36kr.com/n/api/org/list?industry=10&page=";
        }
        RequestHeader[] requestHeaders = {
                new RequestHeader("Host", "rong.36kr.com"),
                new RequestHeader("Connection", "keep-alive"),
                new RequestHeader("Accept", "application/json, text/plain, */*"),
                new RequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"),
                new RequestHeader("X-Tingyun-Id", "Dio1ZtdC5G4;r=88090279"),
                new RequestHeader("Accept-Encoding", "gzip, deflate, br"),
                new RequestHeader("Accept-Language", "zh-CN,zh;q=0.9"),
                new RequestHeader("Cookie", cookie.getCookie())
        };
        //总页数,会更新
        int totalPages = 2;
        //一般超时时间
        int generalTimeout = 1000 * 10;
        Random random = new Random();
        //休眠间隔
        int interval;
        //要被访问的链接
        String usedUrlStr;
        for (int i = 1; i <= totalPages; i++) {
            usedUrlStr = rowUrlStr + i;
            //随机休眠5到10秒
            interval = random.nextInt(1000 * 10) + 1000 * 10;
            try {
                //访问目标地址,获取json字符串
                String text = fetcher.getInfoByGet(usedUrlStr, requestHeaders, generalTimeout, generalTimeout, null);
                //如果账号被暂时封禁,抛出异常
                if (text.contains("您请求的过于频繁，请稍后再试")) {
                    throw new MuchRequestException();
                }
                //如果cookie失效,可能登录期限已过或者账号被永久封禁
                if (text.contains("用户未登录")) {
                    throw new UnLoginException();
                }
                if (flag == 0) {
                    //解析融资项目
                    //获取总页数
                    totalPages = FinanceProjectIdParser.getTotalPages(text);
                    //获取公司id
                    Set<FinanceProjectIdEntity> projectIdSet = FinanceProjectIdParser.getProjectId(text);
                    //插入数据库
                    operator.insertFinanceProjectId(projectIdSet);
                } else if (flag == 1) {
                    //解析投资机构
                    totalPages = InvestOrganizationIdParser.getTotalCount(text);
                    Set<InvestCompanyIdEntity> companyIdSet = InvestOrganizationIdParser.getId(text);
                    operator.insertInvestCompanyId(companyIdSet);
                }
                printlnInfo("访问" + areaId + "第" + i + "页完成,休眠" + interval + "毫秒");
            } catch (MuchRequestException e) {
                printlnErr("emm,正在访问:" + areaId + ",第" + i + "页." + cookie.getLoginName() + "可能被暂时封了");
                cookie.setStatus(1);
                //cookie被封,换cookie
                cookie = cookieRepository.getCookie();
                //如果没有可用cookie,抛出异常
                if (Objects.isNull(cookie)) {
                    throw new NullAvailableCookieException();
                }
                requestHeaders[8] = new RequestHeader("Cookie", cookie.getCookie());
                //随机休眠5到10分钟
                interval = random.nextInt(1000 * 300) + 1000 * 300;
                //i - 1,重新抓取刚刚没有抓到的
                i--;
            } catch (UnLoginException e) {
                printlnErr("耶熊,正在访问:" + areaId + ",第" + i + "页.登录失效," + cookie.getLoginName() + "可能被永久封了");
                cookie.setStatus(2);
                //cookie被封,换cookie
                cookie = cookieRepository.getCookie();
                //如果没有可用cookie,抛出异常
                if (Objects.isNull(cookie)) {
                    throw new NullAvailableCookieException();
                }
                requestHeaders[8] = new RequestHeader("Cookie", cookie.getCookie());
                //封号休眠5到10分钟
                interval = random.nextInt(1000 * 300) + 1000 * 300;
                //i - 1,重新抓取刚刚没有抓到的
                i--;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    //不管成功失败,请求次数 + 1
                    requestedCount++;
                    Thread.sleep(interval);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (requestedCount == 100) {
                //访问400个链接后,主动换cookie,并且请求数置0
                cookie = cookieRepository.getCookie();
                //如果没有可用cookie,抛出异常
                if (Objects.isNull(cookie)) {
                    throw new NullAvailableCookieException();
                }
                printlnInfo("已访问100个链接,切换Cookie:" + cookie.getLoginName());
                requestedCount = 0;
            }
        }
        return cookie;
    }

    /**
     * 获取融资机构id
     */
    void getProjectIdM() {
        String rowUrlStr = "https://rong.36kr.com/api/mobi-investor/label_dict/industry/companys?ccid=&" +
                "labelName=%E5%8C%BB%E7%96%97%E5%81%A5%E5%BA%B7&order=desc&";
        RequestHeader[] requestHeader = {
                new RequestHeader("User-Agent", "36kr-Tou-Android/4.1.2 (MI 6; Android 8.0.0; Scale/1920x1080)"),
                new RequestHeader("Cookie", "kr_plus_id=289153482; kr_plus_token=YnBR45Q1B8Rp1fAaesKNHMirhHOGf8h4633_____; krid_user_version=1"),
                new RequestHeader("Host", "rong.36kr.com"),
                new RequestHeader("Connection", "Keep-Alive"),
                new RequestHeader("Accept-Encoding", "gzip")
        };
        Random random = new Random();
        //一般超时时间
        int generalTimeout = 1000 * 10;
        //访问间隔
        int interval;
        for (int pageIndex = 1; pageIndex <= phonePage; pageIndex++) {
            String urlStr = rowUrlStr + "page=" + pageIndex + "&pageSize=20&type=hot_score&cities=";
            try {
                getProjectIdByPageIndex(urlStr, requestHeader, generalTimeout);
            } catch (MuchRequestException e) {
                printlnInfo("暂时被封禁");
                e.printStackTrace();
                break;
            } catch (OtherException e) {
                printlnInfo("不明原因");
                e.printStackTrace();
                break;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                printlnInfo("第" + pageIndex + "检索完成");
                interval = random.nextInt(1000 * 3) + 1000 * 2;
                try {
                    Thread.sleep(interval);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void getProjectIdByPageIndex(String urlStr, RequestHeader[] requestHeader, int generalTimeout)
            throws MuchRequestException, OtherException {
        try {
            //访问链接,获取数据
            String jsonStr = fetcher.getInfoByGet(urlStr, requestHeader, generalTimeout, generalTimeout, null);
            if (jsonStr.contains(RESPONSE_CODE[1])) {
                throw new MuchRequestException();
            } else if (!jsonStr.contains(RESPONSE_CODE[0])) {
                throw new OtherException("没有获取到正确的内容" + jsonStr);
            }
            //解析json字符串中的项目id
            Set<FinanceProjectIdEntity> idEntitySet = FinanceProjectIdParser.getProjectIdM(jsonStr, null);
            //插入数据库(PS:由于数据量大小,不需要注意插入效率)
            operator.insertFinanceProjectId(idEntitySet);
        } catch (IOException | RequestNotExecutedException e) {
            e.printStackTrace();
        }
    }
}
